package com.ea.service.impl;

import com.ea.common.constant.RedisKeyConstants;
import com.ea.common.enums.AccountStatusEnums;
import com.ea.common.enums.OperationResultCode;
import com.ea.common.exception.ServiceException;
import com.ea.common.util.IdWorker;
import com.ea.common.util.MD5Util;
import com.ea.common.util.RedisUtil;
import com.ea.common.util.TokenUtil;
import com.ea.domain.dto.LoginDTO;
import com.ea.domain.dto.UserCreateDTO;
import com.ea.domain.dto.UserInfoDTO;
import com.ea.domain.dto.UserUpdateDTO;
import com.ea.domain.entity.UserInfo;
import com.ea.mapper.UserInfoMapper;
import com.ea.service.IUserInfoService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author NAME-L
 * @Description TODO
 * @className IUserInfoServiceImpl
 * @date 2023-01-03 21:06:01
 */
@Service
public class IUserInfoServiceImpl implements IUserInfoService {
    private static final Logger logger = LoggerFactory.getLogger(IUserInfoServiceImpl.class);

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public UserInfoDTO loginIn(LoginDTO loginDTO) {
        UserInfoDTO userInfoDTO = new UserInfoDTO();
        String loginAccount = loginDTO.getLoginAccount();
        String loginPassword = loginDTO.getLoginPassword();
        if (StringUtils.isEmpty(loginAccount) && StringUtils.isEmpty(loginPassword)) {
            throw new ServiceException(OperationResultCode.PARAMETER_ERROR);
        }
        UserInfo userInfo = userInfoMapper.selectUserInfoByLoginAccount(loginAccount);
        if (userInfo == null) {
            throw new ServiceException(OperationResultCode.ACCOUNT_NOT_EXISTS);
        }
        if (!AccountStatusEnums.NORMAL.getCode().equals(userInfo.getStatus())) {
            throw new ServiceException(OperationResultCode.ACCOUNT_STATUS_IS_ABNORMAL.getCode(), String.format(OperationResultCode.ACCOUNT_STATUS_IS_ABNORMAL.getMsg(), loginAccount));
        }
        Integer count = (Integer) redisUtil.get(RedisKeyConstants.SUBJECT_KEY + loginAccount + RedisKeyConstants.USER_LOGIN_FAIL);
        if (count == null) {
            count = 0;
        }
        // 先判断是否有输入错误次数记录，5次则不允许登录
        if (count == 5) {
            throw new ServiceException(OperationResultCode.ACCOUNT_IS_FORBIDDEN);
        }
        // 通过MD5工具判断输入的密码与数据库存储的密码是否符合
        boolean conform = MD5Util.verify(loginPassword, userInfo.getLoginPassword());
        if (!conform) {
            logger.error("MD5密码校验失败");
            // 如果不为 0 的情况，说明已经有输入错误的情况
            if (count == 0) {
                redisUtil.set(RedisKeyConstants.SUBJECT_KEY + loginAccount + RedisKeyConstants.USER_LOGIN_FAIL, count, 60 * 10);
            } else {
                long expireTime = redisUtil.getExpire(RedisKeyConstants.SUBJECT_KEY + loginAccount + RedisKeyConstants.USER_LOGIN_FAIL);
                count++;
                redisUtil.set(RedisKeyConstants.SUBJECT_KEY + loginAccount + RedisKeyConstants.USER_LOGIN_FAIL, count, expireTime);
            }
            throw new ServiceException(OperationResultCode.ACCOUNT_PASSWORD_ERROR.getCode(), OperationResultCode.ACCOUNT_PASSWORD_ERROR.getMsg() + "10分钟内可重试次数还剩" + (5 - count) + "次");
        }

        // 记录token
        String token = TokenUtil.tokenGenerator(userInfo.getUserId());
        redisUtil.set(RedisKeyConstants.SUBJECT_KEY + userInfo.getUserId() + RedisKeyConstants.USER_LOGIN_TOKEN, token, 60 * 15);
        // 登录成功后删除失败记录
        boolean hasFail = redisUtil.hasKey(RedisKeyConstants.SUBJECT_KEY + loginAccount + RedisKeyConstants.USER_LOGIN_FAIL);
        if (hasFail) {
            redisUtil.del(RedisKeyConstants.SUBJECT_KEY + loginAccount + RedisKeyConstants.USER_LOGIN_FAIL);
        }
        BeanUtils.copyProperties(userInfo, userInfoDTO);
        userInfoDTO.setToken(token);
        return userInfoDTO;
    }

    @Override
    public void userLoginOut(Long userId) {
        UserInfo userInfo = userInfoMapper.selectUserByUserId(userId);
        if (userInfo == null) {
            throw new ServiceException(OperationResultCode.SYSTEM_ERROR);
        }
        String redisKey = RedisKeyConstants.SUBJECT_KEY + userInfo.getUserId() + RedisKeyConstants.USER_LOGIN_TOKEN;
        if (redisUtil.hasKey(redisKey)) {
            redisUtil.del(redisKey);
        }
    }

    @Override
    public void userRegister(UserCreateDTO createDTO) {
        logger.info("新用户创建，createDTO:{}", createDTO);
        if (createDTO == null) {
            throw new ServiceException(OperationResultCode.SYSTEM_ERROR);
        }
        String loginAccount = createDTO.getLoginAccount();
        Integer countOfLoginAccount = userInfoMapper.selectCountByLoginAccount(loginAccount);
        if (countOfLoginAccount != 0) {
            throw new ServiceException(OperationResultCode.ACCOUNT_IS_EXISTS);
        }
        String phone = createDTO.getPhone();
        Integer phoneCount = userInfoMapper.selectPhoneCount(phone);
        if (phoneCount != 0) {
            throw new ServiceException(OperationResultCode.ACCOUNT_PHONE_IS_BE_REGISTER);
        }
        UserInfo createUserInfo = new UserInfo();
        String encryptPassWord = MD5Util.generate(createDTO.getLoginPassword());
        createUserInfo.setUserId(IdWorker.nextId());
        createUserInfo.setUserName(createDTO.getUserName());
        createUserInfo.setNickName(createDTO.getNickName());
        createUserInfo.setLoginAccount(createDTO.getLoginAccount());
        createUserInfo.setLoginPassword(encryptPassWord);
        createUserInfo.setPhone(createDTO.getPhone());
        createUserInfo.setSex(createDTO.getSex());
        createUserInfo.setImgUrl(createDTO.getImgUrl());
        createUserInfo.setStatus(AccountStatusEnums.NORMAL.getCode());
        userInfoMapper.insertUserInfo(createUserInfo);

    }

    @Override
    public void writeOffUser(Long userId) {
        logger.info("要注销的用户id:{}" , userId);
        UserInfo userInfo = userInfoMapper.selectUserByUserId(userId);
        if (userInfo != null) {
            userInfoMapper.updateUserInfoStatus(userId, AccountStatusEnums.DELETE.getCode());
        }
    }

    @Override
    public void updateUserInfo(UserUpdateDTO updateDTO) {
        if (updateDTO.getUserId() == null) {
            throw new ServiceException(OperationResultCode.PARAMETER_ERROR);
        }
        logger.info("更新用户信息：{}", updateDTO);
        userInfoMapper.updateUserInfo(updateDTO);
    }

    @Override
    public UserInfo getUserInfo(Long userId) {
        return userInfoMapper.selectUserByUserId(userId);
    }

    @Override
    public void changePassword(Long userId, String oldPassword, String newPassword) {
        logger.info("需要修改密码的用户id：{}", userId);
        UserInfo userInfo = userInfoMapper.selectUserByUserId(userId);
        if (userInfo == null) {
            throw new ServiceException(OperationResultCode.ACCOUNT_NOT_EXISTS);
        }
        String redisKey = RedisKeyConstants.SUBJECT_KEY + userInfo.getUserId() + RedisKeyConstants.USER_LOGIN_TOKEN;
        String redisToken = "";
        String requestToken = "";
        if (redisUtil.hasKey(redisKey)) {
            redisToken = String.valueOf(redisUtil.get(redisKey));
        }
        // todo 获取当前发起请求的用户token，与存储在redis中的token相比较，如果不同则不允许修改
        if (!redisToken.equals(requestToken)) {
            throw new ServiceException(OperationResultCode.ACCOUNT_CHANGE_PASSWORD_FORBIDDEN);
        }
        String encryptPassword = userInfo.getLoginPassword();
        boolean success = MD5Util.verify(oldPassword, encryptPassword);
        if (!success) {
            throw new ServiceException(OperationResultCode.ACCOUNT_PASSWORD_ERROR);
        }
        // 新密码
        String newEncryptPassword = MD5Util.generate(newPassword);
        logger.info("修改后加密的密码为：{}", newEncryptPassword);
        userInfoMapper.updatePassWord(userId, newEncryptPassword);
        redisUtil.del(redisKey);
    }
}
